using System;
using System.Collections.Generic;
using System.Linq;
using Sustainalytics.ESGRatings.BusinessLogic.Computing;

using Xunit;
using Xunit.Abstractions;

namespace Sustainalytics.ESGComputing.Service.Tests.IndexingTests
{
    public class IndexingByKeyTest : Disposable, IClassFixture<ComparativeGroupFixture>
    {
        private readonly List<Company> _fakeCompanies = new List<Company>();
        private readonly ITestOutputHelper _output;
        private readonly IndexByKey<Guid, string> _sut;

        public IndexingByKeyTest(ITestOutputHelper output, ComparativeGroupFixture fixture)
        {

            _fakeCompanies.AddRange(fixture.Companies);
            _sut = IndexByKey<Guid, string>.Create(_fakeCompanies, x => x.Id, x => x.Region);
            _output = output;
        }




        public class Given_11_Companies : IndexingByKeyTest
        {
            public Given_11_Companies(ITestOutputHelper output, ComparativeGroupFixture fixture)
                : base(output, fixture)
            {
            }


            [Fact]
            public void Count_Should_Return_11_Keys()
            {
                var expectedCount = _fakeCompanies.Count;
                var actualCount = _sut.Count;
                Assert.Equal(expectedCount, actualCount);
            }



            [Fact]
            public void GetItems_For_Given_Index_should_Return_Key()
            {
                var index = 0;
                Guid actual = _sut.GetItem(index);

                Guid expected = _fakeCompanies.ElementAt(0).Id;
                Assert.Equal(expected, actual);
            }


            [Fact]
            public void GetItems_Index_Out_Of_Range_Throws_Exception()
            {
                var index = _fakeCompanies.Count;
                Action taks = () => _sut.GetItem(index);
                Assert.Throws<IndexOutOfRangeException>(taks);
            }
        }

        public class Given_Companies_Selector : IndexingByKeyTest
        {
            public Given_Companies_Selector(ITestOutputHelper output, ComparativeGroupFixture fixture)
                : base(output, fixture)
            {
            }

            [Fact]
            public void SelectorCount_ShouldReturn_Number_of_Distinct_Regions()
            {
                var expected = _fakeCompanies.Select(x => x.Region).Distinct().Count();
                var actual = _sut.SelectorsCount;
                Assert.Equal(expected, actual);
            }

            [Fact]
            public void GetGroupId_Should_Return_Group_Index_For_Selector()
            {
                var regions = _fakeCompanies.Select(x => x.Region).Distinct().ToList();
                var expected = regions.FindIndex(x => x == _fakeCompanies[0].Region);
                var actual = _sut.GetGroupId(_fakeCompanies[0].Region);
                Assert.Equal(expected, actual);
            }
        }

        public class Given_Companies_Key : IndexingByKeyTest
        {
            public Given_Companies_Key(ITestOutputHelper output, ComparativeGroupFixture fixture)
                : base(output, fixture)
            {
            }

            [Fact]
            public void GetGroupElements_Should_return_all_elements_in_same_group()
            {
                var referenceKey = _fakeCompanies[0].Id;
                var referenceSelection = _fakeCompanies[0].Region;
                var expected = _fakeCompanies.Where(x => x.Region == referenceSelection).Select(x => x.Id);

                var actual = _sut.GetGroupElements(referenceKey);
                Assert.Equal(expected, actual);
            }

            [Fact]
            public void GetGroupElements_Should_return_all_elements_in_the_given_groups()
            {
                var referenceSelectors = _fakeCompanies.Select(x => x.Region).Distinct().Take(2);
                var expected = _fakeCompanies.Where(x => referenceSelectors.Contains(x.Region)).Select(x => x.Id);
                var actual = _sut.GetGroupElements(referenceSelectors);
                Assert.Equal(expected, actual);
            }

            [Theory,
             InlineData(0),
             InlineData(1)]
            public void GetIndex_ForKey_in_set_should_return_index_gte_0(int index)
            {
                var key = _fakeCompanies[index].Id;
                var actual = _sut.GetIndex(key);
                Assert.True(actual >= 0);
            }

            [Fact]
            public void GetIndex_ForKey_NOT_in_set_should_return_index_lt_0()
            {
                var key = Guid.NewGuid();
                var actual = _sut.GetIndex(key);
                Assert.True(actual < 0);
            }

            [Fact]
            public void GetIndex_for_items_in_Set_should_return_indexis_matching_order_in_set()
            {
                var expected = Enumerable.Range(0, _fakeCompanies.Count).ToArray();
                var actual = _sut.GetIndex();
                Assert.Equal(expected, actual);


            }



        }
    }
}